#include <iostream>
using namespace std;
#include "mythread.hpp"
#include "blockqueue.hpp"
#include "task.hpp"
#include <vector>
#include <cstdio>
#include <unistd.h>

#define MAXNUM_PRO 5 // 生产者线程的最大数量
#define MAXNUM_CUS 3 // 消费者线程的最大数量

// 保存阻塞队列的地址和线程名称
template <class T>
struct threadData
{
    blockqueue<T> *que;
    string threadname;
};

// 生产线程要执行的操作
void *handle_pro(void *args)
{
    // 需要从args中获取阻塞队列的地址、线程的名字
    threadData<task> *threaddata = static_cast<threadData<task> *>(args);
    string oper("+-*/%"); // 操作符表
    while (true)
    {
        sleep(3); // 生产者先休眠3秒，再传入数据
        int x = rand() % 10;
        int y = rand() % 10;
        int pi = rand() % oper.size();  // 操作符索引
        char op = oper[pi];             // 操作符
        task newtask(x, y, op, mymath); // 构建任务

        threaddata->que->push(newtask);
        cout << "线程" << threaddata->threadname << "生产了一个任务：" << newtask.whatTodo() << endl;
    }
    return nullptr;
}

// 消费线程要执行的操作
void *handle_cus(void *args)
{
    // 需要从args中获取阻塞队列的地址、线程的名字
    threadData<task> *threaddata = static_cast<threadData<task> *>(args);
    while (true)
    {
        task newtask = threaddata->que->pop();
        cout << "线程" << threaddata->threadname << "处理了一个任务：" << newtask() << endl;
    }
    return nullptr;
}

int main()
{
    srand((unsigned int)time(nullptr)); // 随机数种子

    blockqueue<task> q; // 创建阻塞队列

    vector<thread *> protable; // 存储生产者线程的地址
    vector<thread *> custable; // 存储消费者线程的地址

    vector<threadData<task> *> prodatas; // 存储每个每个生产者线程对应threadData的地址
    vector<threadData<task> *> cusdatas; // 存储每个每个消费者线程对应threadData的地址

    // 1、先创建一批生产线程和一批消费线程

    for (int i = 0; i < MAXNUM_PRO; i++)
    {
        // 格式化线程名
        char namebuffer[64] = {0};
        snprintf(namebuffer, sizeof(namebuffer), "producter %d", i + 1);
        // 为生产者线程创建一个threadData结构
        threadData<task> *newprodata = new threadData<task>;
        newprodata->que = &q;
        newprodata->threadname = namebuffer;
        prodatas.push_back(newprodata);
        // 创建生产者线程，将threadData结构的地址传递给线程的任务函数
        thread *newpro = new thread(handle_pro, (void *)prodatas[i]);
        protable.push_back(newpro);
    }

    for (int i = 0; i < MAXNUM_CUS; i++)
    {
        // 格式化线程名
        char namebuffer[64] = {0};
        snprintf(namebuffer, sizeof(namebuffer), "customer %d", i + 1);
        // 为消费者线程创建一个threadData结构
        threadData<task> *newcusdata = new threadData<task>;
        newcusdata->que = &q;
        newcusdata->threadname = namebuffer;
        cusdatas.push_back(newcusdata);
        // 创建消费者线程，将threadData结构的地址传递给线程的任务函数
        thread *newcus = new thread(handle_cus, (void *)cusdatas[i]);
        protable.push_back(newcus);
    }

    // 2、等待线程、然后销毁线程对象和线程对应的threadData结构

    for (int i = 0; i < MAXNUM_PRO; i++)
    {
        // 先等待线程
        protable[i]->join();
        // 线程等待成功后，再释放线程对象和线程对应的threadData结构
        delete protable[i];
        delete prodatas[i];
    }

    for (int i = 0; i < MAXNUM_CUS; i++)
    {
        // 先等待线程
        custable[i]->join();
        // 线程等待成功后，再释放线程对象和线程对应的threadData结构
        delete custable[i];
        delete cusdatas[i];
    }

    return 0;
}